package org.beanutopia;

import org.beanutopia.reflect.ImportSlot;
import static org.beanutopia.reflect.ImportSlot.importer;
import static org.beanutopia.reflect.Reflector.reader;
import org.beanutopia.slot.MapSlot;
import static org.beanutopia.reflect.ReflectSlot.reflector;
import org.beanutopia.slot.Slot;
import org.beanutopia.htmap.HasProperties;

import java.util.Map;

/**
 * @author ymeymann
 * @since Dec 6, 2007 11:02:09 PM
 */
public class Definitions {

  /* Imported */
  public static <D,V> void defineImported(Property<D,V> property) {
    property.initName();
    defineImported(property, property.namespace(), property.name());
  }

  public static <D,V> void defineImported(Property<D,V> property, Class importSource) {
    property.initName();
    defineImported(property, importSource, property.name());
  }

  public static <D,V> void defineImported(Property<D,V> property, Class importSource, String name) {
    defineImported(property, importSource, name, property.valueType() == Boolean.class ? "is" : "get");
  }

  public static <D,V> void defineImported(Property<D,V> property, Class importSource, String name, String prefix) {
    property.define(ImportSlot.reader(importSource, name, property.beanType(), property.valueType(), prefix).initialize());
  }

  public static <D,V> void defineImported(UpdatableProperty<D,V> property) {
    property.initName();
    defineImported(property, property.namespace(), property.name());
  }

  public static <D,V> void defineImported(UpdatableProperty<D,V> property, Class importSource) {
    property.initName();
    defineImported(property, importSource, property.name());
  }

  public static <D,V> void defineImported(UpdatableProperty<D,V> property, Class importSource, String name) {
    defineImported(property, importSource, name, property.valueType() == Boolean.class ? "is" : "get");
  }

  public static <D,V> void defineImported(UpdatableProperty<D,V> property, Class importSource, String name, String getterPrefix) {
    property.define(importer(importSource, name, property.beanType(), property.valueType(), getterPrefix, "set").initialize());
  }

  /* reflections */
  public static <D,V> void defineReflected(Property<D,V> property) {
    property.initName();
    defineReflected(property, property.name());
  }

  public static <D,V> void defineReflected(Property<D,V> property, String name) {
    defineReflected(property, name, property.valueType() == Boolean.class ? "is" : "get");
  }

  public static <D,V> void defineReflected(Property<D,V> property, String name, String prefix) {
    property.define(reader(property.beanType(), name, property.valueType(), prefix).initialize());
  }

  public static <D,V> void defineReflected(UpdatableProperty<D,V> property) {
    property.initName();
    defineReflected(property, property.name());
  }

  public static <D,V> void defineReflected(UpdatableProperty<D,V> property, String name) {
    defineReflected(property, name, property.valueType() == Boolean.class ? "is" : "get");
  }

  public static <D,V> void defineReflected(UpdatableProperty<D,V> property, String name, String getterPrefix) {
    property.define(reflector(property.beanType(), name, property.valueType(), getterPrefix, "set").initialize());
  }

  /* Maps */
  public static <D extends Map,V> void defineMapped(Property<D,V> property) {
    property.define(MapSlot.<V>reader(property.name()));
  }

  public static <D extends Map,V> void defineMapped(Property<D,V> property, String name) {
    property.define(MapSlot.<V>reader(name));
  }

  public static <D extends Map,V> void defineMapped(UpdatableProperty<D,V> property) {
    property.define(MapSlot.<V>mapper(property.name()));
  }
  public static <D extends Map,V> void defineMapped(UpdatableProperty<D,V> property, String name) {
    property.define(MapSlot.<V>mapper(name));
  }

  /* HTMap */
  public static <D extends HasProperties,V> void defineByHTMap(UpdatableProperty<D,V> property) {
//    if (!HasProperties.class.isAssignableFrom(property.beanType())) {
//      throw new PropertyDefinitionException("The class " + property.beanType() + " does not extend " + HasProperties.class);
//    }
    Slot<HasProperties, V> intraSlot = property.intraAccess();
    property.define(intraSlot);
//    property.projection.addRule(HasProperties.class, intraSlot.getProjection());
//    property.injections.addRule(HasProperties.class, intraSlot.getInjection());
  }

  /* ExtraMap */
  public static <D,V> void defineExtraMapped(UpdatableProperty<D,V> property) {
    Slot<Object, V> extraSlot = property.extraAccess();
    property.define(extraSlot);
  }
}
